home *** CD-ROM | disk | FTP | other *** search
/ PC Graphics Unleashed / PC Graphics Unleashed.iso / ch05 / adapt.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-09  |  41.6 KB  |  1,055 lines

  1. /*...................... ADAPT.C .................. 6-3-94 ....*/
  2. /* This program demonstrates Adaptive Color Palette generation.*/
  3. /* It reads True Color (24 bit per pixel) TARGA images and     */
  4. /* converts them to 8 bit palette images. It also writes out   */
  5. /* the converted image as an 8 bit per pixel Palette Color TIFF*/
  6. /* image.                                                      */
  7. /*.............................................................*/
  8.  
  9. #include<malloc.h>
  10. #include<stdlib.h>
  11. #include <stdio.h>
  12. #include <fcntl.h>
  13. #include <io.h>
  14. #include<conio.h>
  15. #include <math.h>
  16.  
  17. #include<tiff.h>
  18. #include<vsa.h>
  19. #include<vsa_font.h>
  20.  
  21. #ifndef _MSC_VER
  22. /*.....                This is line for Borland C Only !               .....*/
  23. extern unsigned _stklen = 13000;
  24. #endif
  25.  
  26. /*.............................................................*/
  27. /*       Define type struct CUBE first, needed later!          */
  28. /*.............................................................*/
  29. struct CUBE {
  30.     unsigned x0;
  31.     unsigned y0;
  32.     unsigned z0;
  33.     unsigned x1;    // Outside of cube volume!
  34.     unsigned y1;    // Outside of cube volume!
  35.     unsigned z1;    // Outside of cube volume!
  36.     unsigned vol;   // this is the volume of the cube;
  37.     long r_avg;     // this is the Average Red value for cube
  38.     long g_avg;     // this is the Average Green value for cube
  39.     long b_avg;     // this is the Average Blue value for cube
  40.         long fsum;      // this is the sum of frequencies in cube
  41.     long cerr;      // this is the NET color error for the cube
  42.     float fom;      // Figure Of Merit, Worthyness of cube split
  43.   };
  44.  
  45. /*.............................................................*/
  46. /*     All of the function prototypes.                         */
  47. /*.............................................................*/
  48. int  adaptive_lut(char *,unsigned char far *,
  49.                                     unsigned char far *);
  50. int  volume_hist(char *, unsigned char far *);
  51. void analyze_row(unsigned char far *, int, unsigned char far *);
  52. void bisect_cube(struct CUBE far *,struct CUBE far *);
  53. void gen_cube_color_stats(struct CUBE far *,
  54.                                                     unsigned char far *,int);
  55. void validate_cube(struct CUBE far *);
  56. void copy_cube(struct CUBE, struct CUBE far *);
  57. int  add_cube_to_list(struct CUBE, int);
  58. int  delete_cube_from_list(int, int);
  59. int  move_cubes_to_lut(int, unsigned char far *,
  60.                                              unsigned char far *);
  61. void assign_lut_to_colors(unsigned char far *, unsigned);
  62. void replace_pixel(unsigned char *, unsigned, unsigned char *);
  63. void set_adaptive_config(int, int, int);
  64. void get_adaptive_config(int far *, int far *, int far *);
  65. unsigned long read_tga_header(int, int *, int *, int *, int *);
  66. void true_color_lut(void);
  67. void vsa_get_input(char *);
  68. void update_message(int,int,int,char *);
  69. void clear_text_area(int,int,int,int);
  70. void color_bar(int,int);
  71.  
  72. /*.............................................................*/
  73. /*     Global variables which control the show.                */
  74. /*.............................................................*/
  75. int ADPTV_MODE = 0;                 /*..   Default = 0 (Off) ..*/
  76. int ADPTV_NUM_COLORS = 256;         /*..   Default = 256     ..*/
  77. int ADPTV_QUALITY = 50;             /*..   Default = 50      ..*/
  78. struct CUBE CUBE_LIST[256];
  79.  
  80. #define ADPTV_ASSIGN_MODE  0        /*.. This is experimental..*/
  81.  
  82. void main(int argc, char *argv[])
  83. {
  84.     char filename[80],text[100];
  85.     int j,width,height,type,file_handle,orient, colors,r0,c0,vmode;
  86.     unsigned char lut[768], rgb[3072], far *vhist;
  87.     if(argc > 1)
  88. /*.............................................................*/
  89. /*          If specified, set requested video  mode.           */
  90. /*.............................................................*/
  91.         {
  92.             sscanf(argv[1],"%x",&vmode);
  93.             if(vsa_init(vmode) != 0)
  94.                     {
  95.                         printf("Can't set Requested VESA video mode!\n");
  96.                         printf("Is VESA BIOS Extension TSR loaded?\n");
  97.                         return;
  98.                     }
  99.         }
  100.     else
  101. /*.............................................................*/
  102. /*     Otherwise set highest video resolution available.       */
  103. /*.............................................................*/
  104.         {
  105.             if(vsa_init(0x105) != 0)             /* 1024 x 768 x 256 */
  106.                 if(vsa_init(0x103) != 0)           /*  800 x 600 x 256 */
  107.                     if(vsa_init(0x101) != 0)         /*  640 x 480 x 256 */
  108.                         if(vsa_init(0x100) != 0)       /*  640 x 400 x 256 */
  109.                             {
  110.                                 printf("Can't set VESA video mode!\n");
  111.                                 printf("Is VESA BIOS Extension TSR loaded?\n");
  112.                                 return;
  113.                             }
  114.         }
  115. /*.............................................................*/
  116. /*       Set up the Palette as an 8 bit RGB (3,3,2) table.     */
  117. /*       and draw color look up table.                         */
  118. /*.............................................................*/
  119.     true_color_lut();
  120.     tf_set_prime_colors();
  121.     color_bar(0.125*XResolution,0.91*YResolution);
  122. /*.............................................................*/
  123. /*                        Draw Frame around screen.            */
  124. /*.............................................................*/
  125.     vsa_set_color(TF_Yellow);
  126.     vsa_move_to(0,0);
  127.     vsa_rect(XResolution-1,YResolution-1);
  128. /*.............................................................*/
  129. /*            Set up text cursor mode and location.            */
  130. /*.............................................................*/
  131.     vsa_set_text_cursor_mode(1);
  132.     r0 = 0.85*YCharResolution;
  133.     c0 = 0.5*XCharResolution - 17;
  134. /*.............................................................*/
  135. /* Set up the Adaptive Palette controls and get the TARGA      */
  136. /* image file name.                                            */
  137. /*.............................................................*/
  138.     set_adaptive_config(1,256,50);
  139. /*.............................................................*/
  140. /*   Enter main loop which displays Adaptive Palette images.   */
  141. /*.............................................................*/
  142. LOOP:
  143.     update_message(c0*XCharSize,r0*YCharSize,TF_Green,
  144.                                  "Input TARGA Image Filename: ");
  145.     vsa_get_input(filename);
  146.     if(filename[0] == 0)
  147.         goto BAIL;
  148. /*.............................................................*/
  149. /* Allocate memory for the Volume Histogram.  Then run the     */
  150. /* Adaptive Palette code to generate the adaptive palette.     */
  151. /*.............................................................*/
  152.     if(ADPTV_MODE)
  153.         {
  154.             update_message(c0*XCharSize,r0*YCharSize,TF_Violet,
  155.                                          "Please Wait, Calculating Adaptive Palette.");
  156.             if((vhist = (unsigned char far *)halloc(32768,1)) == NULL)
  157.                 {
  158.                     update_message(c0*XCharSize,r0*YCharSize,TF_Red,
  159.                                                  "Can't Allocate Memory for Histogram!");
  160.                     getch();
  161.                     goto BAIL;
  162.                 }
  163.             colors = adaptive_lut(filename,vhist,lut);
  164.             if(colors == -1)
  165.                 {
  166.                     sprintf(text,"Can't find file %s",filename);
  167.                     update_message(c0*XCharSize,r0*YCharSize,TF_Red,text);
  168.                     getch();
  169.                     goto LOOP;
  170.                 }
  171.         }
  172.     update_message(c0*XCharSize,r0*YCharSize,TF_Red,"");
  173. /*.............................................................*/
  174. /* Set up Color Palette either to Adaptive Palette or 8 bit RGB*/
  175. /* and set up prime colors.                                    */
  176. /*.............................................................*/
  177.     if(ADPTV_MODE)
  178.         vsa_write_color_block(0,colors,lut);
  179.     else
  180.         true_color_lut();
  181.     tf_set_prime_colors();
  182. /*.............................................................*/
  183. /*           Open the TARGA file and get header info.          */
  184. /*.............................................................*/
  185.     if((file_handle = open(filename,O_BINARY | O_RDONLY)) == -1)
  186.         {
  187.             sprintf(text,"Can't find file %s",filename);
  188.             update_message(c0*XCharSize,r0*YCharSize,TF_Red,text);
  189.             getch();
  190.             goto LOOP;
  191.         }
  192.     read_tga_header(file_handle,&width,&height,&type,&orient);
  193. /*.............................................................*/
  194. /*              Draw Frame around the new picture.             */
  195. /*.............................................................*/
  196.     vsa_set_color(TF_White);
  197.     vsa_move_to(2,2);
  198.     vsa_rect(width+3,height+3);
  199. /*.............................................................*/
  200. /* Depending on orientation, read file out-top down or         */
  201. /* bottom-up, replace 24 bit pixels with 8 bit palette indexes,*/
  202. /* and display on screen.                                      */
  203. /*.............................................................*/
  204.     if(orient == 32)
  205.         for(j=0;j<height;j++)
  206.             {
  207.                 read(file_handle,rgb,3*width);
  208.                 replace_pixel(rgb,width,vhist);
  209.                 vsa_raster_line(3,width+2,j+3,rgb);
  210.             }
  211.     else
  212.         for(j=height-1;j>=0;j--)
  213.             {
  214.                 read(file_handle,rgb,3*width);
  215.                 replace_pixel(rgb,width,vhist);
  216.                 vsa_raster_line(3,width+2,j+3,rgb);
  217.             }
  218. /*.............................................................*/
  219. /*           Fix up screen frame and color bar.                */
  220. /*.............................................................*/
  221.     vsa_set_color(TF_Yellow);
  222.     vsa_move_to(0,0);
  223.     vsa_rect(XResolution-1,YResolution-1);
  224.     color_bar(0.125*XResolution,0.91*YResolution);
  225. /*.............................................................*/
  226. /*  Close the image file and look for an ESC key to quit.      */
  227. /*  Otherwise, save new image as an 8 bit TIFF called          */
  228. /*  NEW.TIF and LOOP for experimentation with a different      */
  229. /*  input image files.                                         */
  230. /*.............................................................*/
  231.     if(ADPTV_MODE)
  232.         hfree((unsigned char huge *)vhist);
  233.     close(file_handle);
  234.     update_message(c0*XCharSize,r0*YCharSize,TF_Yellow,
  235.                                  "ESC to quit, any other key to continue.");
  236.     if(getch() == 27)
  237.         goto BAIL;
  238.     update_message(c0*XCharSize,r0*YCharSize,TF_Blue,
  239.                                  "Saving image as NEW.TIF.");
  240.     tf_save_file(0,0,width-1,height-1,"new.tif");
  241.     goto LOOP;
  242. BAIL:
  243.     vsa_about();
  244.   getch();
  245.     vsa_set_svga_mode(0x3);
  246.     return;
  247. }                               /*.....       End MAIN    .....*/
  248.  
  249.  
  250. /*....................... READ_TGA_HEADER  ....... 5-17-94 ....*/
  251. /* This routine parses through a TGA header and returns the    */
  252. /* file offset in bytes to the first byte of pixel data.       */
  253. /* It also returns image width, height, and type (type 2 is the*/
  254. /* uncompressed 24 bit image type).                            */
  255. /*.............................................................*/
  256. unsigned long read_tga_header(int handle, int *width,
  257.                               int *height, int *type,
  258.                               int *orientation)
  259. {
  260.   unsigned long offset;
  261.   unsigned char buff[18];
  262.   read(handle,buff,18);
  263.   offset = 18+buff[0];
  264.   *type = buff[2];
  265.  
  266.     *width  = *((unsigned *)buff + 6);
  267.   *height = *((unsigned *)buff + 7);
  268.   *orientation = buff[17];
  269.     return offset;
  270. }                           /*.... END read_tga_header    .....*/
  271.  
  272.  
  273. /*..................... ADAPTIVE_LUT ............ 5-31-94 .....*/
  274. /*    This routine analyzes the True Color image that is       */
  275. /*  defined in the TARGA file called 'filename' and computes   */
  276. /*  an Adpative Color Palette which is an optimal selection of */
  277. /*  the 256 colors for this particular image.  Before calling  */
  278. /*  this routine, 'array' must have been allocated 32768 bytes!*/
  279. /*    First a 32x32x32 volume histogram is computed.  Then the */
  280. /*  volume histogram "cube" is subdivide into up to 255 cubes  */
  281. /*  (color clusters) used in the image.  One more cube in this */
  282. /*  collection is always reserved for BLACK, in RGB space,     */
  283. /*  (this is done so that images which have few colors will    */
  284. /*  still get a good solid BLACK color in the CLUT). Finally   */
  285. /*  it assigns each one of the cubes to each one of the up to  */
  286. /*  256 palette entries, and returns the new Color Look Up     */
  287. /*  Table in the 'color_array'. The function return value is   */
  288. /*  the number of colors in the 'color_array' and is always    */
  289. /*  equal to 256 for now (unused colors set to black).         */
  290. /*    Upon completion, the 32k elements of 'array' store the   */
  291. /*  palette index for their respective true color value (down  */
  292. /*  converted to 15 bits).                                     */
  293. /*.............................................................*/
  294. int  adaptive_lut(char *filename,unsigned char *array,
  295.                   unsigned char *color_array)
  296. {
  297.   int num_cubes,num_vol,colors,most_cubes;
  298.   struct CUBE cube,new_cube;
  299.   most_cubes = ADPTV_NUM_COLORS-1;
  300.   num_cubes = 0;
  301.     if(volume_hist(filename,array) == 1)
  302.         return -1;
  303. /*.............................................................*/
  304. /* Set up the number of cubes to be generated based on uniform */
  305. /* color VOLUME partitioning.  Then, the rest of the cubes are */
  306. /* generated on COLOR ERROR minimizing criteria.               */
  307. /*.............................................................*/
  308.   num_vol = (ADPTV_QUALITY * most_cubes) / 100;
  309. /*.............................................................*/
  310. /*  Initialize first cube.  Get its color stats and put it in  */
  311. /*  cube list.                                                 */
  312. /*.............................................................*/
  313.   if(most_cubes > 0)
  314.     {
  315.       cube.x0 = 0;
  316.       cube.y0 = 0;
  317.       cube.z0 = 0;
  318.       cube.x1 = 32;
  319.       cube.y1 = 32;
  320.       cube.z1 = 32;
  321.       gen_cube_color_stats(&cube,array,1);
  322.       num_cubes = add_cube_to_list(cube,num_cubes);
  323.     }
  324. /*.............................................................*/
  325. /*    Subdivide color volume based on minimizing cube Volumes. */
  326. /*    Do for 'num_vol' cubes.                                  */
  327. /*.............................................................*/
  328.   while(num_cubes < num_vol)
  329.     {
  330.       if(CUBE_LIST[0].vol <= 1)
  331.         break;
  332.       copy_cube(CUBE_LIST[0],&cube);
  333.       bisect_cube(&cube,&new_cube);
  334.       gen_cube_color_stats(&cube,array,1);
  335.       gen_cube_color_stats(&new_cube,array,1);
  336.       num_cubes = delete_cube_from_list(0,num_cubes);
  337.       if(cube.fsum != 0)
  338.         num_cubes = add_cube_to_list(cube,num_cubes);
  339.       if(new_cube.fsum != 0)
  340.         num_cubes = add_cube_to_list(new_cube,num_cubes);
  341.     }
  342. /*.............................................................*/
  343. /* Continue subdividing color volume based on minimizing cube  */
  344. /* color errors.  Do for remaining cubes (up to                */
  345. /* 'ADPTV_NUM_COLORS' - 1).                                    */
  346. /*.............................................................*/
  347.   while(num_cubes < most_cubes)
  348.     {
  349.       if(CUBE_LIST[0].vol <= 1)
  350.         break;
  351.       copy_cube(CUBE_LIST[0],&cube);
  352.       bisect_cube(&cube,&new_cube);
  353.       gen_cube_color_stats(&cube,array,2);
  354.       gen_cube_color_stats(&new_cube,array,2);
  355.       num_cubes = delete_cube_from_list(0,num_cubes);
  356.       if(cube.fsum != 0)
  357.         num_cubes = add_cube_to_list(cube,num_cubes);
  358.       if(new_cube.fsum != 0)
  359.         num_cubes = add_cube_to_list(new_cube,num_cubes);
  360.     }
  361. /*.............................................................*/
  362. /* Add last cube which is BLACK.    Force this cube to top of  */
  363. /* list (via cube.fom = 1000000000) so that it will end up at  */
  364. /* CLUT location 0.                                            */
  365. /*.............................................................*/
  366.   cube.x0 = 0;
  367.   cube.y0 = 0;
  368.   cube.z0 = 0;
  369.   cube.x1 = 1;
  370.     cube.y1 = 1;
  371.   cube.z1 = 1;
  372.   gen_cube_color_stats(&cube,array,1);
  373.   cube.fom = 1000000000.0;
  374.   num_cubes = add_cube_to_list(cube,num_cubes);
  375. /*.............................................................*/
  376. /* Finally, figure out the values for the Color Look Up table. */
  377. /*.............................................................*/
  378.   colors = move_cubes_to_lut(num_cubes,array,color_array);
  379.   return colors;
  380. }                              /*.....  End adaptive_lut  .....*/
  381.  
  382.  
  383. /*.......................... VOLUME_HIST ......... 5-31-94 ....*/
  384. /* This routine analyzes a True Color TARGA image file called  */
  385. /* 'filename' and generates a 3D R-G-B Volume Histogram which  */
  386. /* has 32 elements on a side (32 x 32 x 32). The histogram is  */
  387. /* returned in 'vhist[]'.  Each element of 'vhist[]' stores a  */
  388. /* count from 0 to 255 which is the frequency of occurance for */
  389. /* that color in the image. The count "saturates" at 255.      */
  390. /* ("That color" means the value indexing 'vhist') The value   */
  391. /* indexing 'vhist' is a 15 bit color value whos 5 MSBs are    */
  392. /* Red, then 5 bits Green and finally 5 LSBs of Blue. If the   */
  393. /* image file is not found, this routine returns with a error  */
  394. /* value of 1, else 0.                                         */
  395. /*.............................................................*/
  396. int volume_hist(char *filename, unsigned char far *vhist)
  397. {
  398.     unsigned i;
  399.     int j,width,height,type,file_handle,orient;
  400.     unsigned char rgb[3072];
  401. /*.............................................................*/
  402. /*   Initialize 32x32x32 R-G-B historgram to all zeros.        */
  403. /*.............................................................*/
  404.     for(i=0;i<32768;i++)
  405.         vhist[i] = 0;
  406. /*.............................................................*/
  407. /*           Open the TARGA file and get header info.          */
  408. /*.............................................................*/
  409.     if((file_handle = open(filename,O_BINARY | O_RDONLY)) == -1)
  410.         return 1;
  411.     read_tga_header(file_handle,&width,&height,&type,&orient);
  412. /*.............................................................*/
  413. /*  Read out pixels from TARGA file and process in             */
  414. /*  top down or bottom up order depending on orientation.      */
  415. /*.............................................................*/
  416.   if(orient == 32)
  417.     for(j=0;j<height;j++)
  418.       {
  419.         read(file_handle,rgb,3*width);
  420.         analyze_row(rgb,width,vhist);
  421.       }
  422.   else
  423.     for(j=height-1;j>=0;j--)
  424.       {
  425.         read(file_handle,rgb,3*width);
  426.         analyze_row(rgb,width,vhist);
  427.       }
  428. /*.............................................................*/
  429. /*                   Close the image file.                     */
  430. /*.............................................................*/
  431.   close(file_handle);
  432.   return 0;
  433. }                                /*..... End VOLUME_HIST  .....*/
  434.  
  435.  
  436. /*.................... ANALYZE_ROW ............... 5-31-94 ....*/
  437. /*  This routine takes the pixel data for one row of a 24      */
  438. /*  bit/pixel image and accumulates data in the 32x32x32 R-G-B */
  439. /*  histogram 'vhist'. The pixel data is sent in the 'byte_buf'*/
  440. /*  array.  The number of pixels in the array is defined by    */
  441. /*  'width'.                                                   */
  442. /*.............................................................*/
  443. void  analyze_row(byte_buf,width,vhist)
  444. unsigned char far *byte_buf,far *vhist;
  445. int width;
  446. {
  447.   unsigned sum,i;
  448.   for(i=0;i<width;i++)
  449.     {
  450.       sum = 0;
  451.       sum += (byte_buf[3*i+2])/8 << 10;              /* RED    */
  452.       sum += (byte_buf[3*i+1])/8 << 5;               /* GREEN  */
  453.       sum += (byte_buf[3*i]  )/8;                    /* BLUE   */
  454.       if(vhist[sum] != 255)
  455.         vhist[sum]++;
  456.     }
  457.   return;
  458. }                                /*..... End analyze_row ......*/
  459.  
  460.  
  461. /*........................ BISECT_CUBE ............ 6-1-94 ....*/
  462. /* Input cube in 'cube', output two cubes in 'cube' and        */
  463. /* 'new_cube'.  Cubes are split along longest axis.  New cube  */
  464. /* guaranteed to be "validated".                               */
  465. /*.............................................................*/
  466. void  bisect_cube(struct CUBE *pcube,struct CUBE *pnew_cube)
  467. {
  468.   unsigned dx,dy,dz;
  469.   validate_cube(pcube);
  470. /*.............................................................*/
  471. /*             Get red, green, and blue extent of cube.        */
  472. /*.............................................................*/
  473.   dx = pcube->x1-pcube->x0;                 /* RED dimension   */
  474.   dy = pcube->y1-pcube->y0;                 /* GREEN dimension */
  475.   dz = pcube->z1-pcube->z0;                 /* BLUE dimension  */
  476. /*.............................................................*/
  477. /* If red is longest dimension, split red axis of cube.        */
  478. /*.............................................................*/
  479.   if((dx >= dy) && (dx >= dz))
  480.     {
  481.       pnew_cube->x0 = (pcube->x1 + pcube->x0)/2;
  482.       pnew_cube->y0 = pcube->y0;
  483.       pnew_cube->z0 = pcube->z0;
  484.       pnew_cube->x1 = pcube->x1;
  485.       pnew_cube->y1 = pcube->y1;
  486.       pnew_cube->z1 = pcube->z1;
  487.       pcube->x1 = pnew_cube->x0;
  488.       return;
  489.     }
  490. /*.............................................................*/
  491. /* If green is longest dimension, split green axis of cube.    */
  492. /*.............................................................*/
  493.   if((dy >= dx) && (dy >= dz))
  494.     {
  495.       pnew_cube->x0 = pcube->x0;
  496.       pnew_cube->y0 = (pcube->y1 + pcube->y0)/2;
  497.       pnew_cube->z0 = pcube->z0;
  498.       pnew_cube->x1 = pcube->x1;
  499.       pnew_cube->y1 = pcube->y1;
  500.       pnew_cube->z1 = pcube->z1;
  501.       pcube->y1 = pnew_cube->y0;
  502.       return;
  503.     }
  504. /*.............................................................*/
  505. /* If blue is longest dimension, split blue axis of cube.      */
  506. /*.............................................................*/
  507.   if((dz >= dx) && (dz >= dy))
  508.     {
  509.       pnew_cube->x0 = pcube->x0;
  510.       pnew_cube->y0 = pcube->y0;
  511.       pnew_cube->z0 = (pcube->z1 + pcube->z0)/2;
  512.       pnew_cube->x1 = pcube->x1;
  513.       pnew_cube->y1 = pcube->y1;
  514.       pnew_cube->z1 = pcube->z1;
  515.       pcube->z1 = pnew_cube->z0;
  516.       return;
  517.     }
  518. }                                 /*.... END: bisect_cube .....*/
  519.  
  520.  
  521. /*................. GEN_CUBE_COLOR_STATS ........... 6-1-94 ...*/
  522. /* Compute cube's color statistics (Volume, Frequency Sum,     */
  523. /* Average Color value for Red, Green, and Blue, Color Error,  */
  524. /* and Figure-Of-Merit).  The color error is the sum of        */
  525. /* ABS(color - avg_color)*frequency (sort of) for each color   */
  526. /* in cube.                                                    */
  527. /*.............................................................*/
  528. void  gen_cube_color_stats(struct CUBE *pcube,
  529.                            unsigned char *array,int mode)
  530. {
  531.   unsigned i,j,k,freq;
  532.   int dr,dg,db;
  533.   unsigned long sum,index,index0,color_error;
  534.   long red_avg,grn_avg,blu_avg;
  535.   sum = 0;
  536.   red_avg = 0;
  537.   grn_avg = 0;
  538.   blu_avg = 0;
  539.   color_error = 0;
  540. /*.............................................................*/
  541. /*                        Compute cube's volume                */
  542. /*.............................................................*/
  543.   pcube->vol = (pcube->x1-pcube->x0)*
  544.                (pcube->y1-pcube->y0)*
  545.                (pcube->z1-pcube->z0);
  546. /*.............................................................*/
  547. /*     Compute average red, green and blue values.             */
  548. /*.............................................................*/
  549.   for(k=pcube->x0;k<pcube->x1;k++)
  550.     {
  551.       index0 = k<<10;
  552.       for(j=pcube->y0;j<pcube->y1;j++)
  553.         {
  554.           index = index0 + (j<<5) + pcube->z0;
  555.           for(i=pcube->z0;i<pcube->z1;i++)
  556.             {
  557.               freq = array[index];
  558.               red_avg += k*freq;
  559.               grn_avg += j*freq;
  560.               blu_avg += i*freq;
  561.               sum += freq;
  562.               index ++;
  563.             }
  564.         }
  565.     }
  566.   if(sum != 0)
  567.     {
  568.       red_avg /= sum;
  569.       grn_avg /= sum;
  570.       blu_avg /= sum;
  571.     }
  572.   else
  573.     {
  574.       red_avg = 0;
  575.       grn_avg = 0;
  576.       blu_avg = 0;
  577.     }
  578. /*.............................................................*/
  579. /*     Now compute color error.                                */
  580. /*.............................................................*/
  581.   for(k=pcube->x0;k<pcube->x1;k++)
  582.     {
  583.       index0 = k<<10;
  584.       for(j=pcube->y0;j<pcube->y1;j++)
  585.         {
  586.           index = index0 + (j<<5) + pcube->z0;
  587.           for(i=pcube->z0;i<pcube->z1;i++)
  588.             {
  589.               freq = array[index];
  590.               dr = (red_avg-(int)k);
  591.               dg = (grn_avg-(int)j);
  592.               db = (blu_avg-(int)i);
  593.               color_error += freq*sqrt(dr*dr+dg*dg+db*db);
  594.               index ++;
  595.             }
  596.         }
  597.     }
  598.   pcube->r_avg = red_avg;
  599.   pcube->g_avg = grn_avg;
  600.   pcube->b_avg = blu_avg;
  601.   pcube->fsum  = sum;
  602.   pcube->cerr  = color_error;
  603. /*.............................................................*/
  604. /* You can decide what characteristic is used for the Figure of*/
  605. /* Merit (FOM) by setting 'mode' to 1 - 5.   Modes 1 and 2 are */
  606. /* normally used.  Modes 3, 4, and 5 are for play.             */
  607. /*.............................................................*/
  608.   if(mode == 1)
  609.     pcube->fom  = (float) pcube->vol;
  610.   if(mode == 2)
  611.     pcube->fom  = (float) pcube->cerr;
  612.   if(mode == 3)
  613.     pcube->fom  = (float) pcube->fsum;
  614.   if(mode == 4)
  615.     pcube->fom  = (float)(pcube->cerr)*(float)(pcube->fsum);
  616.   if(mode >= 5)
  617.     pcube->fom  = (float)(pcube->vol-1)*(float)(pcube->fsum);
  618.   return;
  619. }                         /*.... END: gen_cube_color_stats ....*/
  620.  
  621.  
  622. /*.................. MOVE_CUBES_TO_LUT ............ 6-3-94 ....*/
  623. /* This routine loads 'color_array' with the colors of the     */
  624. /* cubes in the 'CUBE_LIST' (after the 'CUBE_LIST' has been    */
  625. /* generated).  The return value is the number of colors       */
  626. /* loaded into 'color_array', and is always 256.               */
  627. /*.............................................................*/
  628. int  move_cubes_to_lut(int num_cubes,unsigned char *array,
  629.                        unsigned char *color_array)
  630. {
  631.   unsigned i;
  632. /*.............................................................*/
  633. /* Compute Color Lookup Table entries. You could do this by    */
  634. /* taking average of each color component axis of cube.  But   */
  635. /* instead ... Use the Average color computed earlier for each */
  636. /* cube! (This is more accurate color).  Multiply by two to    */
  637. /* scale the 5 bit color into the 6 bit palette entry.         */
  638. /*.............................................................*/
  639.   for(i=0;i < num_cubes;i++)
  640.     {
  641.       color_array[3*i+0] = 2*CUBE_LIST[i].r_avg;
  642.       color_array[3*i+1] = 2*CUBE_LIST[i].g_avg;
  643.       color_array[3*i+2] = 2*CUBE_LIST[i].b_avg;
  644.     }
  645. /*.............................................................*/
  646. /*          Set unused palette entries to black.               */
  647. /*.............................................................*/
  648.   for(i=num_cubes;i<256;i++)
  649.     {
  650.       color_array[3*i+0] = 0;
  651.       color_array[3*i+1] = 0;
  652.       color_array[3*i+2] = 0;
  653.     }
  654.   assign_lut_to_colors(array,num_cubes);
  655.   return 256;
  656. }                        /*.....  End move_cubes_to_lut  ......*/
  657.  
  658.  
  659. /*................. ASSIGN_LUT_TO_COLORS ........... 6-3-94 ...*/
  660. /* Assign each of the 32768 color values in the RGB Color Space*/
  661. /* (compressed from 16M) to one of the up to 256 cubes (which  */
  662. /* map to the color lut). 'ADPTV_ASSIGN_MODE' determines the   */
  663. /* way that colors are assigned.  If '0', then color error is  */
  664. /* minimized. Otherwise, all colors which land within a cube   */
  665. /* get set to cube average color.                              */
  666. /*.............................................................*/
  667. void  assign_lut_to_colors(unsigned char *array,unsigned
  668.                                                      num_cubes)
  669. {
  670.   int red,grn,blu,dr,dg,db,error,last_error;
  671.   unsigned i,j,k,n;
  672.   long index,index0;
  673.     if(!ADPTV_ASSIGN_MODE)
  674. /*.............................................................*/
  675. /*  For each color in the image, assign it the CUBE ID for the */
  676. /* CUBE which has the nearest average color value .            */
  677. /*.............................................................*/
  678.     {
  679.       for(i=0;i<32768;i++)
  680.         {
  681.           if(array[i] != 0)
  682.             {
  683.               red = (i & 0x7c00) >> 10;
  684.               grn = (i & 0x03e0) >>  5;
  685.               blu =  i & 0x001f       ;
  686.               last_error = 10000;
  687.               for(n=0;n<num_cubes;n++)
  688.                 {
  689.                   dr = CUBE_LIST[n].r_avg - red;
  690.                   dg = CUBE_LIST[n].g_avg - grn;
  691.                   db = CUBE_LIST[n].b_avg - blu;
  692.                   error = dr*dr+dg*dg+db*db;
  693.                   if(error <= last_error)
  694.                     {
  695.                       array[i] = n;
  696.                       last_error = error;
  697.                     }
  698.                 }
  699.             }
  700.         }
  701.     }
  702. /*.............................................................*/
  703. /* For each cube, assign its ID to all of the colors in        */
  704. /* its boundary. This method is not as accurate as the one     */
  705. /* above but its faster.                                       */
  706. /*.............................................................*/
  707.   else
  708.     {
  709.       for(n=0;n<num_cubes;n++)
  710.         {
  711.           for(k=CUBE_LIST[n].x0;k<CUBE_LIST[n].x1;k++)
  712.             {
  713.               index0 = k<<10;
  714.               for(j=CUBE_LIST[n].y0;j<CUBE_LIST[n].y1;j++)
  715.                 {
  716.                   index = index0 + (j<<5) + CUBE_LIST[n].z0;
  717.                   for(i=CUBE_LIST[n].z0;i<CUBE_LIST[n].z1;i++)
  718.                     {
  719.                       array[index] = n;
  720.                       index ++;
  721.                     }
  722.                 }
  723.             }
  724.         }
  725.     }
  726.   return;
  727. }                         /*.... END: assign_lut_to_colors ....*/
  728.  
  729.  
  730. /*..................... VALIDATE_CUBE ............ 1-12-94 ....*/
  731. /* Insures that cube point 0 is closer to (or same distance    */
  732. /* from) origin of RGB Color than cube point 1.                */
  733. /*.............................................................*/
  734. void  validate_cube(struct CUBE *pcube)
  735. {
  736.   unsigned temp;
  737.   if(pcube->x0 > pcube->x1)
  738.     {
  739.       temp = pcube->x0;
  740.       pcube->x0 = pcube->x1;
  741.       pcube->x1 = temp;
  742.     }
  743.   if(pcube->y0 > pcube->y1)
  744.     {
  745.       temp = pcube->y0;
  746.       pcube->y0 = pcube->y1;
  747.       pcube->y1 = temp;
  748.     }
  749.   if(pcube->z0 > pcube->z1)
  750.     {
  751.       temp = pcube->z0;
  752.       pcube->z0 = pcube->z1;
  753.       pcube->z1 = temp;
  754.     }
  755.   return;
  756. }                                 /*.... END: validate_cube ...*/
  757.  
  758. /*........................... COPY_CUBE .......... 1-15-94 ....*/
  759. /* This routine copies the 'cube' struct to the 'new_cube'     */
  760. /* struct.                                                     */
  761. /*.............................................................*/
  762. void  copy_cube(struct CUBE cube,struct CUBE *pnew_cube)
  763. {
  764.   pnew_cube->x0 = cube.x0;
  765.   pnew_cube->y0 = cube.y0;
  766.   pnew_cube->z0 = cube.z0;
  767.   pnew_cube->x1 = cube.x1;
  768.   pnew_cube->y1 = cube.y1;
  769.   pnew_cube->z1 = cube.z1;
  770.   pnew_cube->vol = cube.vol;
  771.   pnew_cube->r_avg = cube.r_avg;
  772.   pnew_cube->g_avg = cube.g_avg;
  773.   pnew_cube->b_avg = cube.b_avg;
  774.   pnew_cube->fsum  = cube.fsum;
  775.   pnew_cube->cerr  = cube.cerr;
  776.   pnew_cube->fom  = cube.fom;
  777.   return;
  778. }                                 /*....   END: copy_cube  ....*/
  779.  
  780.  
  781. /*..................... ADD_CUBE_TO_LIST ........... 6-1-94 ...*/
  782. /* Adds a cube to list.  Inserts cube appropriately to maintain*/
  783. /* a descending cube sort based on CUBE_LIST[i].fom.  In       */
  784. /* otherwords, CUBE_LIST[0].fom is always going to be the      */
  785. /* largest value of all cubes.  Give this routine a cube and   */
  786. /* the current number of cubes in the list 'n'. Routine returns*/
  787. /* the new value of 'n'.  NOTE: For n cubes, you have          */
  788. /* CUBE_LIST[0] thru CUBE_LIST[n-1].                           */
  789. /*.............................................................*/
  790. int  add_cube_to_list(struct CUBE new_cube,int n)
  791. {
  792.   int m;
  793.   m = n+1;
  794. /*.............................................................*/
  795. /* Bump lower FOM valued cubes down a step in cube list.       */
  796. /*.............................................................*/
  797.   if(n>0)
  798.     while(CUBE_LIST[n-1].fom <= new_cube.fom)
  799.       {
  800.         CUBE_LIST[n].x0 = CUBE_LIST[n-1].x0;
  801.         CUBE_LIST[n].y0 = CUBE_LIST[n-1].y0;
  802.         CUBE_LIST[n].z0 = CUBE_LIST[n-1].z0;
  803.         CUBE_LIST[n].x1 = CUBE_LIST[n-1].x1;
  804.         CUBE_LIST[n].y1 = CUBE_LIST[n-1].y1;
  805.         CUBE_LIST[n].z1 = CUBE_LIST[n-1].z1;
  806.         CUBE_LIST[n].vol = CUBE_LIST[n-1].vol;
  807.         CUBE_LIST[n].r_avg = CUBE_LIST[n-1].r_avg;
  808.         CUBE_LIST[n].g_avg = CUBE_LIST[n-1].g_avg;
  809.         CUBE_LIST[n].b_avg = CUBE_LIST[n-1].b_avg;
  810.         CUBE_LIST[n].fsum = CUBE_LIST[n-1].fsum;
  811.         CUBE_LIST[n].cerr = CUBE_LIST[n-1].cerr;
  812.         CUBE_LIST[n].fom = CUBE_LIST[n-1].fom;
  813.         n--;
  814.         if(n==0) break;
  815.       }
  816. /*.............................................................*/
  817. /*                  Add new cube to cube list.                 */
  818. /*.............................................................*/
  819.   CUBE_LIST[n].x0 = new_cube.x0;
  820.   CUBE_LIST[n].y0 = new_cube.y0;
  821.   CUBE_LIST[n].z0 = new_cube.z0;
  822.   CUBE_LIST[n].x1 = new_cube.x1;
  823.   CUBE_LIST[n].y1 = new_cube.y1;
  824.   CUBE_LIST[n].z1 = new_cube.z1;
  825.   CUBE_LIST[n].vol = new_cube.vol;
  826.   CUBE_LIST[n].r_avg = new_cube.r_avg;
  827.   CUBE_LIST[n].g_avg = new_cube.g_avg;
  828.   CUBE_LIST[n].b_avg = new_cube.b_avg;
  829.   CUBE_LIST[n].fsum = new_cube.fsum;
  830.   CUBE_LIST[n].cerr = new_cube.cerr;
  831.   CUBE_LIST[n].fom = new_cube.fom;
  832.   return m;
  833. }                                 /*.. END: add_cube_to_list ..*/
  834.  
  835.  
  836. /*................... DELETE_CUBE_FROM_LIST ...... 1-15-94 ....*/
  837. /* Deletes cube 'p' from cube list. Bumps up remaining cubes to*/
  838. /* keep list consecutive and hole free.  Give this routine the */
  839. /* current number of cubes in list ('n') and the cube id to be */
  840. /* deleted ('p').  Routine returns the new value of 'n'.       */
  841. /* NOTE: For n cubes, you have CUBE_LIST[0] thru CUBE_LIST[n-1]*/
  842. /*.............................................................*/
  843. int  delete_cube_from_list(int p,int n)
  844. {
  845.   if(p >= n)
  846.     return n;
  847.   while(p < n-1)
  848.     {
  849.       CUBE_LIST[p].x0 = CUBE_LIST[p+1].x0;
  850.       CUBE_LIST[p].y0 = CUBE_LIST[p+1].y0;
  851.       CUBE_LIST[p].z0 = CUBE_LIST[p+1].z0;
  852.       CUBE_LIST[p].x1 = CUBE_LIST[p+1].x1;
  853.       CUBE_LIST[p].y1 = CUBE_LIST[p+1].y1;
  854.       CUBE_LIST[p].z1 = CUBE_LIST[p+1].z1;
  855.       CUBE_LIST[p].vol = CUBE_LIST[p+1].vol;
  856.       CUBE_LIST[p].r_avg = CUBE_LIST[p+1].r_avg;
  857.       CUBE_LIST[p].g_avg = CUBE_LIST[p+1].g_avg;
  858.       CUBE_LIST[p].b_avg = CUBE_LIST[p+1].b_avg;
  859.       CUBE_LIST[p].fsum  = CUBE_LIST[p+1].fsum;
  860.       CUBE_LIST[p].cerr  = CUBE_LIST[p+1].cerr;
  861.       CUBE_LIST[p].fom  = CUBE_LIST[p+1].fom;
  862.       p++;
  863.     }
  864.   return n-1;
  865. }                            /*.. END: delete_cube_from_list ..*/
  866.  
  867. /*..................... REPLACE_PIXEL.C ............ 6-1-94 ...*/
  868. /*  This routine compresses the 3 byte per pixel data in the   */
  869. /*  array 'byte_buf' into a single byte per pixel data array   */
  870. /*  (back into 'byte_buf'). The 8-8-8 RGB pixel is down        */
  871. /*  converted to a 5-5-5 RGB pixel.  Then, the 15 bit RGB      */
  872. /*  pixel value is used to look up in the 'array' to get the 8 */
  873. /*  bit palette index for that color. The number of pixels in  */
  874. /*  'byte_buf' is defined by 'width'.                          */
  875. /*.............................................................*/
  876. void replace_pixel(unsigned char *byte_buf,unsigned 
  877. width,unsigned char *array)
  878. {
  879.   int i,sum;
  880. /*.............................................................*/
  881. /* If Adaptive Palette ON, map pixel to adaptive palette.      */
  882. /*.............................................................*/
  883.   if(ADPTV_MODE)
  884.     for(i=0;i<width;i++)
  885.       {
  886.         sum = 0;
  887.         sum += (byte_buf[3*i+2])/8 << 10;            /* RED    */
  888.         sum += (byte_buf[3*i+1])/8 << 5;             /* GREEN  */
  889.         sum += (byte_buf[3*i]  )/8;                  /* BLUE   */
  890.         byte_buf[i] = array[sum];
  891.       }
  892. /*.............................................................*/
  893. /* If Adaptive Palette OFF, map pixel to 8 bit RGB palette.    */
  894. /*.............................................................*/
  895.   else
  896.     for(i=0;i<width;i++)
  897.       {
  898.         sum = 0;
  899.         sum += (byte_buf[3*i+2])/32 << 5;            /* RED    */
  900.         sum += (byte_buf[3*i+1])/32 << 2;            /* GREEN  */
  901.         sum += (byte_buf[3*i]  )/64;                 /* BLUE   */
  902.         byte_buf[i] = (unsigned char) sum;
  903.       }
  904.   return;
  905. }                    /*.....        End replace_pixel    ......*/
  906.  
  907.  
  908.  
  909. /*................ SET_ADAPTIVE_CONFIG ........... 4-30-94 ....*/
  910. /* This routine configures the operation of the TIFF256 library*/
  911. /* with regards to 24 bit/pixel images.  The input parameters  */
  912. /* have are:                                                   */
  913. /*                                                             */
  914. /*          enable - Adaptive Palette is enabled when enable   */
  915. /*                   is 1. disabled otherwise.                 */
  916. /*      num_colors - This parameter determines how many colors */
  917. /*                   the 16M color image will be reduced to.   */
  918. /*                   (2 min, 256 max!)                         */
  919. /*         quality - This parameter lets the user optimize the */
  920. /*                   Adaptive Palette algorithm for image      */
  921. /*                   quality. The valid range is from 0 to 100.*/
  922. /*                     For values closer to 0, the algorithm is*/
  923. /*                   optimized for images with fewer colors,   */
  924. /*                   but many shades per color (smooth shading */
  925. /*                   is emphasized at the expense of color     */
  926. /*                   variety).                                 */
  927. /*                     For values closer to 100, the algorithm */
  928. /*                   is optimized for images with a broader    */
  929. /*                   color distribution (many colors are       */
  930. /*                   accomodated at the expense of shade       */
  931. /*                   continuity).                              */
  932. /*.............................................................*/
  933. void  set_adaptive_config(int enable,int num_colors,int quality)
  934. {
  935.   ADPTV_MODE         = enable;
  936.   ADPTV_NUM_COLORS   = num_colors;
  937.   ADPTV_QUALITY      = quality;
  938.   if(ADPTV_NUM_COLORS > 256) ADPTV_NUM_COLORS = 256;
  939.   if(ADPTV_NUM_COLORS < 2)   ADPTV_NUM_COLORS = 2;
  940.   if(ADPTV_QUALITY > 100) ADPTV_QUALITY = 100;
  941.   return;
  942. }
  943.  
  944. /*................. GET_ADAPTIVE_CONFIG ............ 4-10-94 ..*/
  945. /*  This routine returns the current Adaptive Palette          */
  946. /* configuration parameters (which were set by                 */
  947. /* set_adaptive_config().                                      */
  948. /*.............................................................*/
  949. void  get_adaptive_config(int *penable,int *pnum_colors,
  950.                           int *pquality)
  951. {
  952.   *penable      = ADPTV_MODE;
  953.   *pnum_colors  = ADPTV_NUM_COLORS;
  954.   *pquality     = ADPTV_QUALITY;
  955.   return;
  956. }
  957.  
  958. /*.................... TRUE_COLOR_LUT.C .......... 5-15-94 ....*/
  959. /* This routine generates a 'true color' LUT.  An 8 bit index  */
  960. /* into the LUT represents 3 bits of RED, 3 bits of GREEN, and */
  961. /* 2 bits of BLUE.  The 3 msbs of the 8 bit index are the RED  */
  962. /* field, next 3 are GREEN, and the 2 lsbs are the BLUE field. */
  963. /*                                                             */
  964. /*.............................................................*/
  965. void true_color_lut(void)
  966. {
  967.   int i;
  968.   unsigned char color_array[768];
  969.   for(i=0;i<256;i++)
  970.     {
  971.             color_array[3*i+0]= ((i & 0x00e0) >> 5) * 9;
  972.             color_array[3*i+1]= ((i & 0x001c) >> 2) * 9;
  973.             color_array[3*i+2]= (i & 0x0003) * 21;
  974.         }
  975.     vsa_write_color_block(0,256,color_array);
  976.     return;
  977. }                          /*.....   End true_color_lut   .....*/
  978.  
  979. void update_message(int x0,int y0,int color,char *text)
  980. {
  981.     clear_text_area(x0,y0,51,TF_Black);
  982.     vsa_write_string(x0,y0,color,text);
  983.     vsa_set_color(color);
  984.     return;
  985. }
  986.  
  987. void clear_text_area(int x0,int y0,int length,int color)
  988. {
  989.     vsa_set_color(color);
  990.     vsa_move_to(x0,y0);
  991.     vsa_rect_fill(x0+length*XCharSize-1,y0+YCharSize-1);
  992.     return;
  993. }
  994.  
  995. /*.......................... VSA_GET_INPUT .................... 6-25-94 ....*/
  996. /*  This routine reads the keyboard input and echos it to the screen until  */
  997. /* a carriage return is entered. Then the whole text string is returned     */
  998. /* via 'text'.                                                              */
  999. /*..........................................................................*/
  1000. void vsa_get_input(char *text)
  1001. {
  1002.     int i,x,y;
  1003.     char key;
  1004.     vsa_get_text_cursor(&x,&y);
  1005.     i=0;
  1006.     text[0] = 0;
  1007.     key = getch();
  1008.     while((key != 13) && (key != 27))                     /*  Do until a return          */
  1009.         {                                        /*  or an ESCAPE Key is hit.   */
  1010.             if(key != 8)
  1011.                 {                                    /*  If not a back space        */
  1012.                     text[i] = key;                     /*  add key entry to string.   */
  1013.                     text[i+1] = 0;
  1014.                     vsa_write_string(x,y,TF_White,text);/*  Echo the updated string.   */
  1015.                     i++;
  1016.                 }
  1017.             else
  1018.                 {                                    /*  If a back space            */
  1019.                     if(i > 0) i --;                    /*  delete last key entry.     */
  1020.                     text[i] = 92;
  1021.                     vsa_write_string(x,y,TF_White,text);/*  Echo the updated string.   */
  1022.                     text[i] = 0;
  1023.                 }
  1024.             key = getch();
  1025.         }
  1026.     return;
  1027. }
  1028.  
  1029. void color_bar(x0,y0)
  1030. int x0,y0;
  1031. {
  1032.     int i;
  1033.     unsigned xx,yy,a,b;
  1034.     float c;
  1035.     xx = XResolution;
  1036.     yy = YResolution;
  1037. /*..........................................................................*/
  1038. /*     Draw outline for color bar.                                          */
  1039. /*..........................................................................*/
  1040.     vsa_set_color(15);
  1041.     vsa_move_to(x0-1,y0-1);
  1042.     a = .75*xx;
  1043.     b = .065*yy;
  1044.     vsa_rect(x0+a+1,y0+b+1);
  1045.     c = (float)a/256;
  1046.     for(i=0;i<256;i++)
  1047.         {
  1048.             vsa_set_color((unsigned char)i);
  1049.             vsa_move_to(x0+(unsigned)(i*c),y0);
  1050.             vsa_rect_fill(x0+(unsigned)(c+i*c),y0+b);
  1051.         }
  1052.     return;
  1053. }
  1054.  
  1055.